\documentclass[12pt]{report}
\usepackage{geometry}
\geometry{a4paper,scale=0.8}
\usepackage{longtable}
\usepackage{url}
\usepackage{listings}
\usepackage{graphicx}
\usepackage{float}
\usepackage{titlesec}
\usepackage{CJK}
\begin{CJK}{UTF8}{gkai}
\titleformat{\chapter}[hang]{\centering\Huge\bfseries}{\chaptername}{1em}{}
%\renewcommand{\chaptername}{第\CJKnumber{thechapter}章}
\title{A007 学习linux0.01加载和运行应用程序}
\author{Chuzy}
\date{2020/05/03}

\lstset{
    basicstyle          =   \small\ttfamily,          % 基本代码风格
    keywordstyle        =   \bfseries,          % 关键字风格
    commentstyle        =   \rmfamily\itshape,  % 注释的风格，斜体
    stringstyle         =   \ttfamily,  % 字符串风格
    flexiblecolumns,                % 别问为什么，加上这个
    numbers             =   left,   % 行号的位置在左边
    showspaces          =   false,  % 是否显示空格，显示了有点乱，所以不现实了
    numberstyle         =   \ttfamily,    % 行号的样式，小五号，tt等宽字体
    showstringspaces    =   false,
    captionpos          =   t,      % 这段代码的名字所呈现的位置，t指的是top上面
    frame               =   lrtb,   % 显示边框
    columns         =   flexible,     
    breaklines      =   true,
    language        =  [ANSI]C,
    escapeinside=``
}


%以上部分叫做"导言区",下面才开始写正文
\begin{document}

%先插入标题
\maketitle
\tableofcontents
\newpage 
\chapter{概述}
在004版本中，我们初始化了硬盘和文件管理系统，在005和006版本中，我们解决了编译我们自己的应用并将他们复制到磁盘映像上的问题。本章我们就加载和执行刚刚编译出来应用，打印出我们第一个Hello World。
\begin{figure}[htbp]
\centering
\includegraphics[scale=0.5]{fig/37.png}
\caption{007版本运行结果}
\label{fig:Version 007}
\end{figure}
\chapter{流程图}
为了保持清晰，相对004版本没有变化的文件，将不在列出。
\begin{longtable}{|l|l|p{5cm}|p{3cm}|} 
\caption{007版本的文件}  
\label{tab: 007 files} \\ 
\hline 目录&文件名&作用&变化 \\
\hline / & Makefile & 增加了lib目录下文件的处理 & 变化\\ 
\hline  /init & main.c & 增加了trap和sched的初始化 & 变化\\
\hline  /kernel & Makefile & 增加了新的内核文件 & 变化\\
\hline  
\end{longtable}  

\chapter{初始化流程控制源码init/main.c}
\section{init函数}
主要的变化在于打印出"Ok"后，我们通过fork创建新的子进程，在子进程中通过调用execve来执行bin/sh，即我们在006中复制到磁盘映像中的文件。
\begin{lstlisting}[breaklines]
void init(void)
{
	int i,j;

	setup();
//	if (!fork())
//		_exit(execve("/bin/update",NULL,NULL));
	(void) open("/dev/tty0",O_RDWR,0);
	(void) dup(0);
	(void) dup(0);
	printf("Ok, printf from user mode.\n\r");	
	printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS,
		NR_BUFFERS*BLOCK_SIZE);
	printf(" Ok.\n\r");
	if ((i=fork())<0)
		printf("Fork failed in init\r\n");
	else if (!i) {
		close(0);close(1);close(2);
		setsid();
		(void) open("/dev/tty0",O_RDWR,0);
		(void) dup(0);
		(void) dup(0);
		_exit(execve("/bin/sh",argv,envp));
	}
	j=wait(&i);
	printf("child %d died with code %04x\n",j,i);
	sync();
	_exit(0);	/* NOTE! _exit, not exit() */
}
\end{lstlisting}

\chapter{进程创建源码kernel/fork.c}
\section{verify\_area 函数}
这个函数相对原始版本的变化在变量start前增加了volatile修饰，避免编译结果出错。
\begin{lstlisting}[breaklines]
void verify_area(void * addr,int size)
{
	volatile unsigned long start;

	start = (unsigned long) addr;
	size += start & 0xfff;
	start &= 0xfffff000;
	start += get_base(current->ldt[2]);
	while (size>0) {
		size -= 4096;
		write_verify(start);
		start += 4096;
	}
}
\end{lstlisting}
\chapter{头文件}
\section{include/linuxsched.h}
\subsection{\_get\_base 定义}
这个函数相对原始版本的变化在增加了寄存器bx的操作，如果不这么修改的话，编译器对edx寄存器的使用会出错。
\begin{lstlisting}[breaklines]
#define _get_base(addr) ({\
unsigned long __base; \
__asm__ __volatile__("movw %1,%%bx\n\t" \
	"movb %3,%%dh\n\t" \
	"movb %2,%%dl\n\t" \
	"shll $16,%%edx\n\t" \
	"movw %%bx,%%dx" \
	:"=d" (__base) \
	:"m" (*((addr)+2)), \
	 "m" (*((addr)+4)), \
	 "m" (*((addr)+7))); \
__base;})
\end{lstlisting}
\chapter{模拟运行}
\section{准备硬盘映像文件}
用006中准备好的硬盘映像文件替换我们之前使用的C4M。
\section{跟踪调试sh程序}
\subsection{获得do\_execve地址}
通过System.map，我们可以获得do\_execve 函数加载的地址。
\begin{lstlisting}[breaklines]
0x000000000000d998                do_execve
\end{lstlisting} 
前面我们知道，内核代码段地址为0x08, 因此do\_execve 的入口即为0x08:0xd998.
\subsection{反汇编并跟踪sh代码}
在do\_execve 结束前，会将eip指向新加载的代码. 在中断响应函数处理结束后，就会跳转到新的代码。我们已经知道，我们在编译新代码(sh)的时候，已经将入口的偏移地址设置为0。因此，新代码的入口函数为0x0f:0x0.

\end{CJK}
\end{document}
